//
// Created by F1997 on 2024/10/21.
//

#include "PcapUtils.h"
#include "headers.hpp"

void PcapUtils::readPcapFile() {
    std::cout << "read pcap file" << std::endl;

    char errbuff[PCAP_ERRBUF_SIZE];
    const char *filename = "/tmp/tmp.FdUHWCPhyb/data/out500_ens3f0__00030_20241017124001.pcap"; // 替换为你的pcap文件名

    // 打开pcap文件
    pcap_t *pcap = pcap_open_offline(filename, errbuff);
    if (pcap == nullptr) {
        std::cerr << "无法打开文件: " << errbuff << std::endl;
        //return 1;
    }

    // 读取和打印 pcap文件中的每个数据包
    const u_char *packet;
    struct pcap_pkthdr header; // pcap 包头部
    int i= 0;
    while ((packet = pcap_next(pcap, &header)) != nullptr) {
        //std::cout << "得到数据包，长度: " << header.len << " caplen/数据包物理长度: " << header.caplen << " 时间: " << header.ts.tv_sec << "." << header.ts.tv_usec << std::endl;
        // 打印更多的数据包信息
/*        for (int i = 0; i < header.caplen; i++) {
            printf("%02x ", packet[i]); // 以十六进制显示原始载荷数据
        }*/

        compareTraffic.writeTraffic1(header);
        //CompareTraffic::writeTraffic1(header);
        i++;

        //packet = pcap_next(pcap, &header);
    }

    // 关闭 pcap 句柄
    pcap_close(pcap);
    std::cout << "包总数" << i << std::endl;

    //return 0;

}


// 检查是否到达计数起点
bool PcapUtils::checkIsStart(std::chrono::system_clock::time_point startTime, int offset) {
    // 当前时间是否 >= 启动时间 + 偏移时间
    // 获取当前时间作为启动时间
    //return (std::chrono::system_clock::now() >= startTime + std::chrono::seconds(offset)) ? true : false;
    return (std::chrono::system_clock::now() >= startTime + std::chrono::seconds(offset)); // 简化后
}


// 从网络接口读取数据包并解析 = 会报错，对‘pcap_open’未定义的引用
/*int PcapUtils::readNetworkInterface() {
    pcap_if_t *alldevs;
    pcap_t *handle;
    char errbuf[PCAP_ERRBUF_SIZE];
    const unsigned char *packet; // 指向的是包的原始数据载荷
    struct pcap_pkthdr header; // 包头中包含了时间戳、捕获长度和原始数据长度等信息
    int res;

     // 获取网络接口
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
        return 1;
    }

     // 打开网络接口
    handle = pcap_open(alldevs->name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf);
    if (handle == NULL) {
        fprintf(stderr, "Couldn't open device %s: %s\n", alldevs->name, errbuf);
        pcap_freealldevs(alldevs);
        return 2;
    }

    pcap_freealldevs(alldevs);

     // 读取并解析网络包
    // pcap_next_ex()函数会阻塞等待，直到捕获到一个网络包或超时
    //if (pcap_loop(handle, 10, packet_handler, reinterpret_cast<u_char*>(&compareTraffic)) < 0)  // pcap_loop()函数会阻塞等待，直到捕获到一个网络包或超时
    while ((res = pcap_next_ex(handle, reinterpret_cast<pcap_pkthdr **>(&header), &packet)) >= 0) {
        if (res == 0)
             // 超时
            continue;

         // 包头信息
        printf("Timestamp: %lld.%lld\n", header.ts.tv_sec, header.ts.tv_usec);
        printf("Caplen: %d\n", header.caplen);
        printf("Len: %d\n", header.len);

         // 有效载荷
        printf("Packet: ");
        for (int i = 0; i < header.caplen; i++) {
            printf("%02x ", packet[i]);
        }
        printf("\n");
    }

    if (res == -1) {
        fprintf(stderr, "Error reading the packets: %s\n", pcap_geterr(handle));
        return 3;
    }

    pcap_close(handle);
    return 0;

}*/


int PcapUtils::readNetworkIface2() {
    char errbuff[PCAP_ERRBUF_SIZE];

    // 基于指定的设备接口创建一个pcap句柄 = 需要root权限，执行时要加 sudo
    pcap_t *pcap = pcap_open_live("ens33", 65536, 1, 1, errbuff);
    if (pcap == nullptr) {
        std::cerr << "无法打开文件: " << errbuff << std::endl;
        return 1;
    }


    // 读取和打印 pcap文件中的每个数据包
    const u_char *packet;
    struct pcap_pkthdr header; // pcap 包头部
    while ((packet = pcap_next(pcap, &header)) != nullptr) {
        std::cout << "得到数据包，长度: " << header.len << " caplen/数据包物理长度: " << header.caplen << " 时间: " << header.ts.tv_sec << "." << header.ts.tv_usec << std::endl;
    }

    // 关闭 pcap 句柄
    pcap_close(pcap);

    return 0;
}

// 参考
// https://blog.csdn.net/xuan196/article/details/135538876
// 从网卡获取
int PcapUtils::readNetworkIfaceLine1(std::chrono::system_clock::time_point startTime, IniReader* iniCfg) {
    // 从配置文件中获取配置
    std::string device = iniCfg->ReadString("LINE_1", "device","");
    std::string sourceIP = iniCfg->ReadString("LINE_1", "sourceIP","");
    std::string destinationIP = iniCfg->ReadString("LINE_1", "destinationIP","");
    std::string destinationPort = iniCfg->ReadString("LINE_1", "destinationPort","");
    std::string filterStr = iniCfg->ReadString("LINE_1", "filterStr","");

    std::string target_time_all = iniCfg->ReadString("MODE", "target_time_all","");

    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_t *handle;

    // 网络接口名称
    const char *dev = device.c_str(); //"ens33";

    // 创建 pcap 句柄
    handle = pcap_create(dev, errbuf);
    if (handle == NULL) {
        fprintf(stderr, "无法创建 pcap 句柄: %s\n", errbuf);
        return 1;
    }

    // 设置纳秒级时间戳精度
    if (pcap_set_tstamp_precision(handle, PCAP_TSTAMP_PRECISION_NANO) != 0) {
        fprintf(stderr, "设置纳秒级时间戳精度失败: %s\n", pcap_geterr(handle));
        // 也可以选择在这里退出，如果纳秒级别时间戳是必需的
    }

    // 激活 pcap 句柄
    if (pcap_activate(handle) != 0) {
        fprintf(stderr, "激活 pcap 句柄失败: %s\n", pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }

    // 设置过滤器
    struct bpf_program fp;
    char filter_exp2[] = "host 192.168.192.1"; // 过滤IP数据包，等价的写法，c_str 后为 const char *
    const char *filter_exp = filterStr.c_str() ;
    if (pcap_compile(handle, &fp, filter_exp, 0, PCAP_NETMASK_UNKNOWN) < 0) {
        fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
        exit(EXIT_FAILURE);
    }
    if (pcap_setfilter(handle, &fp) < 0) {
        fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
        exit(EXIT_FAILURE);
    }

    // 检查实际的时间戳精度
    int tstamp_precision = pcap_get_tstamp_precision(handle);

    struct pcap_pkthdr *header;
    const u_char *packet;

    try {
        auto currentTime = std::chrono::system_clock::now();
        // 当前时间小于指定时间，则忙等
        while ( currentTime.time_since_epoch().count() <= parse_date_to_timestamp(target_time_all)*1000000000 ) {
            // 当前时间小于目标时间，等待100毫秒后再次检查
            //std::this_thread::sleep_for(std::chrono::milliseconds(1));
            std::this_thread::sleep_for(std::chrono::nanoseconds(10)); // 10ns
            currentTime = std::chrono::system_clock::now();
            //std::cout << "1 忙等中。。。" << std::endl;
        }

        //std::cout << "target_time_all:" << target_time_all << std::endl;
        // 1732261744411 954913 = 19位，纳秒
        // 1732221000000 000000
        // 1732257000 = 10位，需要补9个0
        //std::cout << "tm:" << currentTime.time_since_epoch().count()
        //<< " my1:" << string_to_time_point(target_time_all).time_since_epoch().count()
        //<< " my2:" << parse_date_to_timestamp(target_time_all)
        //<< " t&f:" << (currentTime.time_since_epoch().count() <= parse_date_to_timestamp(target_time_all)*1000000000) << std::endl;
        std::cout << "1 pcap开始抓取数据包 tm:" << currentTime.time_since_epoch().count() << " > " << parse_date_to_timestamp(target_time_all)*1000000000 << std::endl;
        // 捕获循环
        while (pcap_next_ex(handle, &header, &packet) >= 0 ) {
            //std::cout << "得到数据包，长度: " << header->len << " caplen/数据包物理长度: " << header->caplen << " 时间: " << header->ts.tv_sec << "." << header->ts.tv_usec << std::endl;
            compareTraffic.writeTraffic1(*header);
        }

    } catch (const std::invalid_argument& e) {
        std::cerr << "Caught an invalid argument: " << e.what() << std::endl;
        // 处理异常，例如提供错误信息、重试操作或者返回错误码
        return -1;
    }

    pcap_close(handle);
    return 0;
}


int PcapUtils::readNetworkIfaceLine2(std::chrono::system_clock::time_point startTime, IniReader* iniCfg) {
    // 从配置文件中获取配置
    std::string device = iniCfg->ReadString("LINE_2", "device","");
    std::string sourceIP = iniCfg->ReadString("LINE_2", "sourceIP","");
    std::string destinationIP = iniCfg->ReadString("LINE_2", "destinationIP","");
    std::string destinationPort = iniCfg->ReadString("LINE_2", "destinationPort","");
    std::string filterStr = iniCfg->ReadString("LINE_2", "filterStr","");
    //std::cout << "2 过滤规则 " << filterStr << std::endl;

    std::string target_time_all = iniCfg->ReadString("MODE", "target_time_all","");


    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_t *handle;

    // 网络接口名称
    const char *dev = device.c_str(); //"ens33";

    // 创建 pcap 句柄
    handle = pcap_create(dev, errbuf);
    if (handle == NULL) {
        fprintf(stderr, "无法创建 pcap 句柄: %s\n", errbuf);
        return 1;
    }

    // 设置纳秒级时间戳精度
    if (pcap_set_tstamp_precision(handle, PCAP_TSTAMP_PRECISION_NANO) != 0) {
        fprintf(stderr, "设置纳秒级时间戳精度失败: %s\n", pcap_geterr(handle));
        // 也可以选择在这里退出，如果纳秒级别时间戳是必需的
    }

    // 激活 pcap 句柄
    if (pcap_activate(handle) != 0) {
        fprintf(stderr, "激活 pcap 句柄失败: %s\n", pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }

    // 设置过滤器
    struct bpf_program fp;
    const char *filter_exp = filterStr.c_str() ;
    if (pcap_compile(handle, &fp, filter_exp, 0, PCAP_NETMASK_UNKNOWN) < 0) {
        fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
        exit(EXIT_FAILURE);
    }
    if (pcap_setfilter(handle, &fp) < 0) {
        fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
        exit(EXIT_FAILURE);
    }

    // 检查实际的时间戳精度
    int tstamp_precision = pcap_get_tstamp_precision(handle);

    struct pcap_pkthdr *header;
    const u_char *packet;


    // 计数起点，停止标记不存在
    auto currentTime = std::chrono::system_clock::now();
    // 当前时间小于指定时间，则忙等
    //while (currentTime.time_since_epoch() <= startTime.time_since_epoch() + std::chrono::seconds(5)) {
    while ( currentTime.time_since_epoch().count() <= parse_date_to_timestamp(target_time_all)*1000000000 ) {
        // 当前时间小于目标时间，等待100毫秒后再次检查
        //std::this_thread::sleep_for(std::chrono::milliseconds(1));
        std::this_thread::sleep_for(std::chrono::nanoseconds(10));
        currentTime = std::chrono::system_clock::now();
        //std::cout << "2 忙等中。。。" << std::endl;
    }

    std::cout << "2 pcap开始抓取数据包" << std::endl;
    // 捕获循环
    while (pcap_next_ex(handle, &header, &packet) >= 0) {
        //std::cout << "得到数据包，长度: " << header->len << " caplen/数据包物理长度: " << header->caplen << " 时间: " << header->ts.tv_sec << "." << header->ts.tv_usec << std::endl;
        compareTraffic.writeTraffic2(*header);
    }
    pcap_close(handle);
    return 0;
}

// 从网卡获取
// pcap_loop(fp, 0, dispatcher_handler, (u_char*)this); // 捕获数据包，并传递给回调函数处理
int PcapUtils::readNetworkIface4() {
    pcap_t* handle = nullptr;
    char errbuf[PCAP_ERRBUF_SIZE];
    const int timeout = 10; // 捕获数据包的超时时间（毫秒）

    printf("pcap_open_live\n");
    handle = pcap_open_live("ens33", 4 * BUFSIZ, 1, timeout, errbuf);
    if (handle == NULL) {
        printf("Couldn't open device\n");
        return -1;
    }

    //std::string str_filter = "ip dst host " + std::string(mc_ip) + " and dst port " + std::to_string(mc_port);
    std::string str_filter = "" ; // "ip dst host 10.0.2.2 and src port 29375";
    struct bpf_program fp;
    printf("pcap_compile\n");
    if (pcap_compile(handle, &fp, str_filter.c_str(), 0, PCAP_NETMASK_UNKNOWN) == -1) {
        printf("Couldn't parse filter\n");
        return -1;
    }
    printf("pcap_setfilter\n");
    if (pcap_setfilter(handle, &fp) == -1) {
        printf("Couldn't install filter \n");
        return -1;
    }

    // 包捕获的回调函数
    auto packet_handler = [](u_char *user_data, u_char* temp, const struct pcap_pkthdr* header, const u_char* pkt_data) -> void
    {
        // user_data 可以用于传递用户数据，这里传递的是 CompareTraffic 对象的指针
        CompareTraffic *compareTraffic = reinterpret_cast<CompareTraffic*>(user_data);
        IPv4Header ipv4(pkt_data);

        printf("%s.%lu SrcIP:%s, DstIP:%s\n", Stamp2Time(header->ts.tv_sec).c_str(), header->ts.tv_usec, ipv4.SrcIP.c_str(), ipv4.DstIP.c_str());
        sleep(1);
        compareTraffic->writeTraffic1(*header);
    };

    struct PacketHandlerArgs {
        u_char* temp;
        const struct pcap_pkthdr* header;
        const u_char* pkt_data;
        CompareTraffic compareTraffic;
    };
    struct PacketHandlerArgs args;
    args.temp = (u_char*)this;
    args.compareTraffic = compareTraffic;

    // 使用 pcap_loop 捕获数据包
    // reinterpret_cast<u_char*>(&compareTraffic)   (u_char*)&args
/*    if (pcap_loop(handle, -1, packet_handler, reinterpret_cast<u_char*>(&compareTraffic) ) < 0) {
        fprintf(stderr, "pcap_loop failed: %s\n", pcap_geterr(handle));
        exit(EXIT_FAILURE);
    }*/

    // pcap_loop和callback之间参数存在联系，pcap_loop的最后一个参数user是留给用户使用的，当callback被调用的时候这个值会传递给callback的第一个参数(也叫user)，
    // callback的最后一个参数p指向一块内存空间，这个空间中存放的就是pcap_loop抓到的数据包

    printf("pcap_close\n");
    pcap_close(handle);
}


// 读队列
int PcapUtils::readMsg(std::shared_ptr<spdlog::logger> _my_logger, IniReader* iniCfg) {
    //std::cout << "readMsg" << std::endl;
    my_logger->info("readMsg");
    compareTraffic.readTraffic1(my_logger);
    return 0;
}

// 采样统计
int PcapUtils::sampled(std::shared_ptr<spdlog::logger> _my_logger, IniReader* iniCfg) {
    //std::cout << "readMsg" << std::endl;
    my_logger->info("采样统计");
    compareTraffic.sampledAndSave(my_logger);
    return 0;
}



// 解析时间字符串并转换为 time_point =
// tm ---> time_t ---> time_point
/*std::chrono::system_clock::time_point PcapUtils::parseTime(const std::string& timeStr) {
    std::tm tm = {};
    std::istringstream ss(timeStr);

    // 使用 std::get_time 解析时间字符串
    ss << std::get_time(&tm, "%Y-%m-%d %H:%M:%S");

    if (ss.fail()) {
        throw std::runtime_error("Failed to parse time string");
    }

    // 将 struct tm 转换为 time_t
    std::time_t time_t_value = mktime(&tm);

    // 将 time_t 转换为 system_clock::time_point
    auto duration = std::chrono::system_clock::from_time_t(time_t_value);

    // 处理纳秒部分
    std::string nanosecondsStr = timeStr.substr(20);
    std::istringstream nsStream(nanosecondsStr);
    long long nanoseconds;
    nsStream >> nanoseconds;

    // 将纳秒部分添加到 duration
    duration += std::chrono::nanoseconds(nanoseconds);

    return duration;
}*/


// 将时间字符串转换为 time_point
std::chrono::system_clock::time_point PcapUtils::string_to_time_point(const std::string& str) {
    // 解析字符串
    int year, month, day, hour, minute, second;
    std::istringstream ss(str);
    char delimiter;

    ss >> year >> delimiter >> month >> delimiter >> day >> delimiter
       >> hour >> delimiter >> minute >> delimiter >> second;

    // 创建 tm 结构体
    std::tm tm_struct = {};
    tm_struct.tm_year = year - 1900; // 年份从 1900 开始计算
    tm_struct.tm_mon = month - 1;     // 月份从 0 开始计算
    tm_struct.tm_mday = day;
    tm_struct.tm_hour = hour;
    tm_struct.tm_min = minute;
    tm_struct.tm_sec = second;

    // 转换为 time_t
    std::time_t time_t_value = mktime(&tm_struct);

    // 转换为 time_point
    return std::chrono::system_clock::from_time_t(time_t_value);
}

// 时间日期转时间戳
// 解析日期字符串并转换为时间戳
time_t PcapUtils::parse_date_to_timestamp(const std::string& date_str) {
    struct tm time_struct = {};
    const char* format = "%Y-%m-%d %H:%M:%S";

    // 解析日期字符串
    if (strptime(date_str.c_str(), format, &time_struct) == nullptr) {
        std::cerr << "Invalid date format:" << date_str.c_str() << ", " << format << std::endl;
        throw std::invalid_argument("日期格式无效");
    }

    // 将 tm 结构体转换为时间戳
    time_t timestamp = mktime(&time_struct);
    if (timestamp == -1) {
        throw std::runtime_error("Failed to convert to timestamp");
    }

    return timestamp;
}


// 解析数据包
void PcapUtils::parse_packet(const struct pcap_pkthdr* pkthdr, const u_char* packet) {
    // 解析以太网头部
    struct my_ether_header *eth_hdr = (struct my_ether_header *)packet;
    if (ntohs(eth_hdr->ether_type) == 0) {
        // 解析IP头部
        struct my_ip_header *ip_hdr = (struct my_ip_header *)(packet + sizeof(struct my_ether_header));
        if (ip_hdr->ip_p == IPPROTO_TCP) {
            // 解析TCP头部
            struct my_tcp_header *tcp_hdr = (struct my_tcp_header *)(packet + sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4));

            // 计算payload的起始位置
            int payload_offset = sizeof(struct my_ether_header) + (ip_hdr->ip_hl * 4) + (tcp_hdr->th_off * 4);
            int payload_size = pkthdr->len - payload_offset;

            // 打印基本信息
            printf("Source IP: %s\n", inet_ntoa(ip_hdr->ip_src));
            printf("Destination IP: %s\n", inet_ntoa(ip_hdr->ip_dst));
            printf("Source Port: %d\n", ntohs(tcp_hdr->th_sport));
            printf("Destination Port: %d\n", ntohs(tcp_hdr->th_dport));
            printf("Payload Size: %d bytes\n", payload_size);

            // 打印payload
            if (payload_size > 0) {
                printf("Payload:\n");
                for (int i = 0; i < payload_size; ++i) {
                    printf("%02x ", packet[payload_offset + i]);
                    if ((i + 1) % 16 == 0) {
                        printf("\n");
                    }
                }
                printf("\n");
            }
        }
    }
}
